home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 1 Issue 2
/
PDCD-1 - Issue 02.iso
/
_utilities
/
utilities
/
003
/
_gs
/
!GS
/
c
/
gdevarc
< prev
next >
Wrap
Text File
|
1991-12-06
|
22KB
|
722 lines
/* Copyright (C) 1991 David Elworthy. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies. */
/* gdevarc.c */
/* Ghostscript driver for Acorn Archimedes (RISC OS) */
/* The code here does output to a sprite, but none of the sprite display,
or the general user interface stuff.
*/
/*
1.0 -- April 1991 -- First working version
2.0 -- June 1991 -- Changed for GhostScript 2.2
2.1 -- Nov 1991 -- Some code moved to gp_arc
Split into two parts
If the symbol GS$Options is defined, up to four floats may be read from it, setting
height and width in inches, and the x and y resolutions in dpi. The output is in the
current mode. Finally, there may be an integer, which is non zero for save only mode.
The defaults are A4, and 180 dpi. Specifying -1 for a variable gets you the default.
From version 2.2, a menu for the read and print windows allows this to be altered.
From version 2.0 onwards, the size of the window is changed to fit the sprite, and
the sprite is redraw manually, rather than being attached to an icon.
If the size is too large or the resolution too high, GS may fall over, probably not
cleanly.
*/
#include <stdlib.h>
#include <string.h>
#include "wimpc.h"
#include "gx.h"
#include "gsmatrix.h" /* for gxdevice.h */
#include "gxbitmap.h"
#include "gxdevice.h"
/* Do all allocation directly */
#include "malloc_.h"
#include "h.os"
#include "bbc.h"
#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "wimpio.h"
#include "colourtran.h"
#include "sprite.h"
#include "werr.h"
#include "saveas.h"
#include "gdevarc.h"
#include "gp_arc.h"
/*--- Device description --- */
/* See gxdevice.h for the definitions of the procedures. */
/* Define all the procedures to start with - we can go back
to defaults for any we turn out not to use */
dev_proc_open_device(arc_open);
dev_proc_get_initial_matrix(arc_get_initial_matrix);
dev_proc_sync_output(arc_sync_output);
dev_proc_output_page(arc_output_page);
dev_proc_close_device(arc_close);
dev_proc_map_rgb_color(arc_map_rgb_color);
dev_proc_map_color_rgb(arc_map_color_rgb);
dev_proc_fill_rectangle(arc_fill_rectangle);
dev_proc_tile_rectangle(arc_tile_rectangle);
dev_proc_copy_mono(arc_copy_mono);
dev_proc_copy_color(arc_copy_color);
dev_proc_draw_line(arc_draw_line);
dev_proc_fill_trapezoid(arc_fill_trapezoid);
dev_proc_tile_trapezoid(arc_tile_trapezoid);
/* The device descriptor */
static gx_device_procs arc_procs = {
arc_open,
arc_get_initial_matrix,
arc_sync_output,
arc_output_page,
arc_close,
arc_map_rgb_color,
arc_map_color_rgb,
arc_fill_rectangle,
arc_tile_rectangle,
arc_copy_mono,
arc_copy_color,
arc_draw_line,
arc_fill_trapezoid,
arc_tile_trapezoid
};
gx_device_arc gs_arc_device = {
sizeof(gx_device_arc),
&arc_procs,
"arc",
/* Width and height are actually in OS units, not pixels */
0, 0, /* width and height in pixels */
0, 0, /* density (pixels per inch) */
no_margins, /* margins around imageable area (inches) */
0, /* has_color */
255, /* max_rgb_value */
1, /* bits per color pixel */
0, /* not open yet */
0, /* mode */
1, 1, /* OS/pixel factors */
-1, /* Page window handle */
NULL, /* Address of sprite area */
0, /* Space allocated */
{0, 0}, /* Sprite id - gets filled in later */
{1, 1, 1, 1}, /* Sprite scaling */
0, /* Are we in a 256 colour mode? */
TRUE, /* Page closed flag */
0 /* Save only mode? */
/* Palette table follows */
};
/*--- Miscellaneous macros, parameters and globals ---*/
#define CheckPoint if (!gp_arc_fast) wimpc_checkpoint()
#if (0)
/* Client name for allocators */
char *arc_client = "GS_ARC";
#endif
/* Code marked with the define AllowModeChange allows the sprite mode to be
changed. This code is not complete; I decided not to finish it because it slows
things down too much. I think all that needs doing is fixing the set_colour macro
*/
#undef AllowModeChange
/*------------------------------- Device control code -------------------------*/
/* Open the device: do any initialization associated with making the
device instance valid. This must be done before any output to the device.
We assume output will be in the current mode, and set the relevant parameters.
*/
int arc_open(gx_device *dev)
{
adev_needed(dev);
float width = -1, height = -1;
float xres = -1, yres = -1;
int saveonly = 0;
int r0, r1;
int mode;
char opt_buffer[MAX_OPTIONS + 1];
/* Find current mode */
mode = wimpt_mode();
/* Read settings from options string, if it exists */
os_read_var_val("GS$Options", opt_buffer, MAX_OPTIONS);
sscanf(opt_buffer, "%f %f %f %f %d", &width, &height, &xres, &yres, &saveonly);
arc_set_options(adev, mode, width, height, xres, yres);
dev->has_color = (bbc_modevar(mode, bbc_NColour) > 1);
dev->bits_per_color_pixel = 1 << bbc_modevar(mode, bbc_Log2BPP);
adev->save_only = (saveonly != 0);
/* There are no modes with more than 8bpp at present, but we should warn
in case of future expansion. Copy color won't work for more than this
*/
if (dev->bits_per_color_pixel > 8)
{
printf("Warning: there are more than 8 bits per pixel\n");
printf("The current implementation may fail on some colour images\n");
}
if (!arc_create_op(arc_dev(dev)))
{
werr(0, "Unable to create output (probably short of memory)");
return 1;
}
/* First create the window and register event handler */
{ char title[100];
arc_set_title(adev, title);
if ((adev->page_window = w_create(title, "page", NULL)) == -1)
return 0;
}
win_register_event_handler(adev->page_window, arc_page_event_handler, adev);
gp_arc_device_menu(arc_device_options, adev);
/* Claim unknown events on the page window, so we can get palette changes */
win_claim_unknown_events (adev->page_window);
/* Set the extent to the size of the sprite */
arc_set_window_extent(adev);
/* Set the close string */
w_close_string(NULL);
dev->is_open = 1;
return 0;
}
/* Construct the initial transformation matrix mapping user
coordinates (nominally 1/72" per unit) to device coordinates.
The Archimedes has (0,0) at the bottom left. Hence we end up with
positive xx and yy values, and zero for everything else.
*/
void arc_get_initial_matrix(gx_device *dev, gs_matrix *mat)
{
mat->xx = dev->x_pixels_per_inch/72.0;
mat->xy = 0;
mat->yx = 0;
mat->yy = dev->y_pixels_per_inch/72.0;
mat->tx = 0;
mat->ty = 0;
}
/* Synchronize the device. If any output to the device has been
buffered, send / write it now. Note that this may be called several times
in the process of constructing a page, so printer drivers should NOT
implement this by printing the page.
OPTIONAL
*/
int arc_sync_output(gx_device *dev)
{
/* Use this as a chance for a checkpoint */
wimpc_checkpoint();
return gx_default_sync_output(dev);
}
/* Output a fully composed page to the device. The default
definition just calls sync_output. Printer drivers should implement this
by printing and ejecting the page.
*/
int arc_output_page(gx_device *dev)
{
adev_needed(dev);
/* Check for save only */
if (adev->save_only)
{
saveas(file_type, "GSOutput", adev->sarea_size, arc_saver, 0, 0, (void *)adev);
}
else
{
/* Put up the window if not save only */
wimp_wstate state;
event_attachmenumaker(adev->page_window, arc_create_page_menu,
arc_process_page_menu, (void *)adev);
if (wimpt_complain(wimp_get_wind_state(adev->page_window, &state)) == 0)
{
state.o.behind = -1; /* Make sure window is opened in front */
wimpt_noerr(wimp_open_wind(&state.o));
adev->page_closed = FALSE;
}
/* Wait until closed */
while (!adev->page_closed) event_process();
wread_fake_input("\n");
}
return 0;
}
/* Close the device: release any associated resources. After this,
output to the device is no longer allowed.
For notes on memory bug, see arc_create_op
*/
int arc_close(gx_device *dev)
{
adev_needed(dev);
wimpt_noerr(wimp_delete_wind(adev->page_window));
win_activedec();
if (adev->sarea)
free((char *)(adev->sarea));
adev->sarea_size = 0;
adev->is_open = 0;
return 0;
}
/* Map a RGB color to a device color. The default algorithm simply
returns the brightness, i.e. max(red, green, blue). The range of legal
values of the RGB arguments is given by the max_rgb_value element of the
device parameter structure. Ghostscript assumes that for devices that
have color capability (i.e., has_color is true), map_rgb_color returns a
color index for a gray level (as opposed to a non-gray color) iff red =
green = blue.
*/
gx_color_index arc_map_rgb_color(gx_device *dev, unsigned short red,
unsigned short green, unsigned short blue)
{
adev_needed(dev);
wimp_paletteword rgb;
int colour;
rgb.bytes.red = red;
rgb.bytes.blue = blue;
rgb.bytes.green = green;
#ifdef AllowModeChange
rgb.bytes.gcol = 0;
return ((gx_color_index)rgb.word);
#else
colourtran_return_GCOLformode(rgb, adev->mode,
(wimp_paletteword *)adev->paltab, &colour);
return ((gx_color_index)colour);
#endif
}
/* Map a device color code to RGB values. The default algorithm
simply sets all three elements of the result to the color.
OPTIONAL
*/
int arc_map_color_rgb(gx_device *dev, gx_color_index color,
unsigned short rgb[3])
{
return gx_default_map_color_rgb(dev, color, rgb);
}
/*------------------------------- Rendering code --------------------------*/
/*--- Low level functions and macros ---*/
/* Start and end output: direct output to sprite, and save context */
/* This is quite inefficient */
static sprite_state sstate;
#define start_op sprite_outputtosprite(adev->sarea, &(adev->sprite), (int *)0, &sstate);
#define end_op sprite_restorestate(sstate);
static void arc_set_colour(gx_device_arc *dev, int col)
{
#ifdef AllowModeChange
wimp_paletteword pw;
pw.word = col;
colourtran_return_GCOLformode(pw, dev->mode,
(wimp_paletteword *)dev->paltab, &col);
#endif
if (arc_is_col256(dev))
{
bbc_gcol(0, col >> 2);
bbc_tint(2, col & 3);
}
else
bbc_gcol(0, col);
}
static void out25(char c1,int x,int y)
{ bbc_vdu(25); bbc_vdu(c1), bbc_vduw(x), bbc_vduw(y); }
/* Macros to adjust coordinates: pixel to OS */
#define adjust_coord(x,y) x = x << adev->xeig; y = y << adev->yeig
#define adjust_x(x) x = x << adev->xeig
#define fg(x,y) sprite_writepixel(adev->sarea, &(adev->sprite), x, y, &col1)
#define bg(x,y) sprite_writepixel(adev->sarea, &(adev->sprite), x, y, &col0)
#define set_colour(d) if (d != last_col) { if (arc_is_col256(adev)) { col1.colour = d >> 2; \
col1.tint = (d & 3) << 6; } else col1.colour = d; last_col = d; }
/*--- Device interface routines ---*/
/* Fill a rectangle with a color. The set of pixels filled is
{(px,py) | x <= px < x + width and y <= py < y + height}. If width <= 0
or height <= 0, nothing should be drawn.
We must subtract 1 from width and height, because bbc-rectangle includes
both limits.
*/
int arc_fill_rectangle(gx_device *dev, int x, int y,
int width, int height, gx_color_index color)
{
adev_needed(dev);
CheckPoint;
if (x + width > dev->width) width = dev->width - x;
if (y + height > dev->height) height = dev->height - y;
if (width <=0 || height <= 0) return 0;
start_op
arc_set_colour(arc_dev(dev), (int)color);
adjust_coord(x,y);
adjust_coord(width,height);
out25(4,x,y);
out25(97,width-1,height-1);
end_op
return 0;
}
/* Draw a minimum-thickness line from (x0,y0) to (x1,y1). The
precise set of points to be filled is defined as follows. First, if y1 <
y0, swap (x0,y0) and (x1,y1). Then the line includes the point (x0,y0)
but not the point (x1,y1).
*/
#define swap(a, b, temp) temp=a;a=b;b=temp;
int arc_draw_line(gx_device *dev, int x0, int y0, int x1, int y1,
gx_color_index color)
{
adev_needed(dev);
CheckPoint;
if (y1 < y0)
{ int temp;
swap(x0, x1, temp);
swap(y0, y1, temp);
}
start_op
arc_set_colour(arc_dev(dev), (int)color);
adjust_coord(x0,y0);
adjust_coord(x1,y1);
out25(4,x0,y0);
out25(13,x1,y1);
end_op
return 0;
}
/* Fill a trapezoid whose corners are (x0,y0), (x0+width0,y0),
(x1,y1), and (x1+width1,y1), where y0 < y1. If width0 < 0, width1 < 0,
width0 = width1 = 0, or y0 >= y1, nothing should be drawn; however, width0
= 0 or width1 = 0 is valid, and should result in a triangle being filled.
The points (x0+width0,y0) and (*,y1) are not included.
??? I think this comment underspecifies the display.
We implement this by drawing two triangles: (x0,y0):(x0+w0-1,y0):(x1,y1-1)
and (x0+w0-1,y0):(x1,y1-1):(x1+w1-1,y1-1): the limit pixels are thus excluded
(subject to rounding errors). If width1 = 0, we omit the second. If width0=0,
we plot (x0,y0):(x1,y1-1):(x1+w1-1,y1-1).
The result is stlightly inaccurate (but so is the default routine).
*/
int arc_fill_trapezoid(gx_device *dev, int x0, int y0, int width0,
int x1, int y1, int width1, gx_color_index color)
{
adev_needed(dev);
CheckPoint;
if (width0 < 0 || width1 < 0 || (width0 == 0 && width1 == 0) || y0 >= y1)
return 0;
start_op
arc_set_colour(arc_dev(dev), (int)color);
adjust_coord(x0,y0);
adjust_coord(x1,y1);
adjust_x(width0);
adjust_x(width1);
out25(4,x0,y0);
out25(4,x1,y1-1);
if (width0 == 0)
{
/* Plot one triangle only */
out25(85,x1+width1-1,y1-1);
}
else
{
/* Plot first triangle */
out25(85,x0+width0-1,y0);
if (width1 != 0)
/* Plot second triangle */
out25(85,x1+width1-1,y1-1);
}
end_op
return 0;
}
/* Copy a monochrome image (similar to the PostScript image
operator). Each scan line is raster bytes wide. Copying begins at
(data_x,0) and transfers a rectangle of the given width at height to the
device at device coordinate (x,y). (If the transfer should start at some
non-zero y value in the data, the caller can adjust the data address by
the appropriate multiple of the raster.) The copying operation writes
device color color0 at each 0-bit, and color1 at each 1-bit: if color0 or
color1 is gx_no_color_index, the device pixel is unaffected if the image
bit is 0 or 1 respectively.
This operation is the workhorse for most of Ghostscript, so
implementing it efficiently is very important.
I tried various experiments, expressing this code in other ways. But they all
take about the same time.
*/
int arc_copy_mono(gx_device *dev, unsigned char *data, int data_x,
int raster, int x, int y, int width, int height,
gx_color_index color0, gx_color_index color1)
{
adev_needed(dev);
int base_x = x;
int h, w;
int plot0 = (color0 != gx_no_color_index);
int plot1 = (color1 != gx_no_color_index);
unsigned char *from;
int d, m, c;
int part_add, part_x;
sprite_colour col0, col1;
CheckPoint;
/* Set fg and bg colours */
if (arc_is_col256(adev))
{
col0.colour = (int)color0 >> 2;
col0.tint = ((int)color0 & 3) << 6;
col1.colour = (int)color1 >> 2;
col1.tint = ((int)color1 & 3) << 6;
}
else
{
col0.colour = (int)color0;
col1.colour = (int)color1;
}
start_op
part_add = data_x / 8;
part_x = 8 - (data_x % 8);
/* Scan across each line in turn */
for (h = 0 ; h < height ; h++, data += raster)
{
x = base_x;
from = data + part_add;
w = width;
/* Display any partial byte, if data_x is not a multiple of 8 */
if (part_x != 8) /* Yes, that really is 8 and not 0 */
{
d = *from++;
m = 1 << part_x;
for ( ; w > 0 && m != 0 ; w--, m = m >> 1, x++)
{
c = d & m;
if ((c != 0) && plot1) /* 1 bit */
fg(x,y);
else if ((c == 0) && plot0) /* 0 bit */
bg(x,y);
}
}
/* Display remaining bytes */
while (w > 0)
{
d = *from++;
for (m = 0x80 ; w > 0 && m != 0 ; w--, m = m >> 1, x++)
{
/* See what bit we have */
c = d & m;
if ((c != 0) && plot1) /* 1 bit */
fg(x,y);
else if ((c == 0) && plot0) /* 0 bit */
bg(x,y);
}
}
/* Next screen line */
y++;
}
end_op
return 0;
}
/* Tile a rectangle. Tiling consists of doing multiple copy_mono
operations to fill the rectangle with copies of the tile. The tiles are
aligned with the device coordinate system, to avoid "seams".
If color0 and color1 are both gx_no_color_index, then the tile is
a color pixmap, not a bitmap: see the next section.
OPTIONAL
*/
int arc_tile_rectangle(gx_device *dev, gx_bitmap *tile,
int x, int y, int width, int height,
gx_color_index color0, gx_color_index color1,
int phase_x, int phase_y)
{
return gx_default_tile_rectangle(dev,tile,x,y,width,height,color0,color1,
phase_x, phase_y);
}
/* Tile a trapezoid similarly.
OPTIONAL
*/
int arc_tile_trapezoid(gx_device *dev, gx_bitmap *tile,
int x0, int y0, int width0, int x1, int y1, int width1,
gx_color_index color0, gx_color_index color1,
int phase_x, int phase_y)
{
return gx_default_tile_trapezoid(dev,tile,x0,y0,width0,x1,y1,width1,color0,color1,
phase_x, phase_y);
}
/* Copy a color image with multiple bits per pixel. The raster is in
bytes, but x and width are in pixels, not bits.
Do this by plotting pixels in the sprite.
This routine is untested. It will only work with less than or equal to
8 bits per pixel. However, you can't get more than that on the Archimedes
at present, using standard hardware.
*/
int arc_copy_color(gx_device *dev, unsigned char *data, int data_x,
int raster, int x, int y, int width, int height)
{
adev_needed(dev);
int bpp = dev->bits_per_color_pixel;
int h,w;
unsigned char *from;
sprite_colour col1;
gx_color_index last_col = gx_no_color_index;
int base_x = x;
char d;
CheckPoint;
start_op
/* For 8bpp, one byte = one colour, so we can optimise */
if (bpp == 8)
{
from = data;
/* Scan across each line in turn */
for (h = 0 ; h < height ; h++, data += raster)
{
x = base_x;
for (w = width, x = base_x ; w > 0 ; w--, x++)
{
d = *from++;
set_colour(d);
fg(x,y);
}
/* Next screen line */
y++;
}
}
else
{
int initial_mask = (bpp == 1) ? 0x80 :
(bpp == 2) ? 0xc0 : 0xf0;
int byte_part = 8 >> (bpp-1);
int part_add, part_x, m;
/* The following loop is similar to copy_mono */
part_add = data_x / byte_part;
part_x = data_x % byte_part;
/* Scan across each line in turn */
for (h = 0 ; h < height ; h++, data += raster)
{
x = base_x;
from = data + part_add;
w = width;
/* Display any partial byte */
if (part_x != 0)
{
d = *from++;
m = initial_mask >> byte_part;
for ( ; w > 0 && m != 0 ; w--, m = m >> bpp, x++)
{
set_colour(d & m);
fg(x,y);
}
}
}
/* Display remaining bytes */
while (w > 0)
{
d = *from++;
for (m = initial_mask ; w > 0 && m != 0 ; w--, m = m >> bpp, x++)
{
set_colour(d & m);
fg(x,y);
}
}
/* Next screen line */
y++;
}
end_op
return 0; /* No default */
}